package cn.numeron.common.viewbinding

import android.view.LayoutInflater
import android.view.View
import androidx.activity.ComponentActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import androidx.viewbinding.ViewBinding
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

inline fun <reified VB : ViewBinding> Fragment.viewBinding() = viewBinding(VB::class.java)

fun <VB : ViewBinding> Fragment.viewBinding(clazz: Class<VB>): ReadOnlyProperty<Fragment, VB> {
    return FragmentViewBindingDelegate(clazz)
}

inline fun <reified VB : ViewBinding> ComponentActivity.viewBinding() = viewBinding(VB::class.java)

fun <VB : ViewBinding> ComponentActivity.viewBinding(clazz: Class<VB>): ReadOnlyProperty<ComponentActivity, VB> {
    return ActivityViewBindingDelegate(clazz)
}

private class ActivityViewBindingDelegate<VB : ViewBinding>(vbClass: Class<VB>) :
    ReadOnlyProperty<ComponentActivity, VB>, LifecycleEventObserver {

    private var _viewBinding: VB? = null

    private val inflateMethod = vbClass.getMethod("inflate", LayoutInflater::class.java)

    @Suppress("UNCHECKED_CAST")
    override fun getValue(thisRef: ComponentActivity, property: KProperty<*>): VB {
        if (_viewBinding == null) {
            _viewBinding = inflateMethod.invoke(null, thisRef.layoutInflater) as VB
            thisRef.lifecycle.addObserver(this)
        }
        return _viewBinding!!
    }

    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        if (event == Lifecycle.Event.ON_DESTROY) {
            source.lifecycle.removeObserver(this)
            _viewBinding = null
        }
    }

}

private class FragmentViewBindingDelegate<VB : ViewBinding>(vbClass: Class<VB>) :
    ReadOnlyProperty<Fragment, VB>, LifecycleEventObserver {

    private var _viewBinding: VB? = null

    private val bindMethod = vbClass.getMethod("bind", View::class.java)

    @Suppress("UNCHECKED_CAST")
    override fun getValue(thisRef: Fragment, property: KProperty<*>): VB {
        if (_viewBinding == null) {
            _viewBinding = bindMethod.invoke(null, thisRef.requireView()) as VB
            thisRef.viewLifecycleOwner.lifecycle.addObserver(this)
        }
        return _viewBinding!!
    }

    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        if (event == Lifecycle.Event.ON_DESTROY) {
            source.lifecycle.removeObserver(this)
            _viewBinding = null
        }
    }

}